//
// Created by fanghr on 18-3-27.
//

#include <cstdlib>
#include "../include/binder_cpp.h"

binder_io *binder_cpp::access::get_binder_io(binder_cpp::io &obj) {
    return obj.bio;
}

binder_state *binder_cpp::access::get_state(binder_cpp::binder &obj) {
    return obj.bs;
}

binder_cpp::binder::binder(size_t mapsize) {
    bs = binder_open(mapsize);
    if (bs == nullptr)throw nullptr;
}

binder_cpp::binder::~binder() {
    binder_close(bs);
}

int binder_cpp::binder::call(binder_cpp::io &msg, binder_cpp::io &reply, uint32_t target, uint32_t code) {
    return binder_call(bs, msg.bio, reply.bio, target, code);
}

void binder_cpp::binder::done(binder_cpp::io &msg, io &reply) {
    binder_done(bs, msg.bio, reply.bio);
}

void binder_cpp::binder::acquire(uint32_t target) {
    binder_acquire(bs, target);
}

void binder_cpp::binder::release(uint32_t target) {
    binder_release(bs, target);
}

void binder_cpp::binder::link_to_death(int32_t target, struct binder_death *death) {
    binder_link_to_death(bs, static_cast<uint32_t>(target), death);
}

binder_cpp::io::io(void *data, size_t maxdata, size_t maxobjects) {
    bio = (binder_io *) malloc(sizeof(binder_io));
    bio_init(bio, data, maxdata, maxobjects);
}

binder_cpp::io::~io() {
    free(bio);
}

void binder_cpp::io::put_obj(void *ptr) {
    bio_put_obj(bio, ptr);
}

void binder_cpp::io::put_ref(uint32_t handle) {
    bio_put_ref(bio, handle);
}

void binder_cpp::io::put_uint32(uint32_t n) {
    bio_put_uint32(bio, n);
}

void binder_cpp::io::put_string16(const uint16_t *str) {
    bio_put_string16(bio, str);
}

void binder_cpp::io::put_string16_x(const char *str) {
    bio_put_string16_x(bio, str);
}

uint32_t binder_cpp::io::get_uint32() {
    return bio_get_uint32(bio);
}

uint16_t *binder_cpp::io::get_string16(size_t *sz) {
    return bio_get_string16(bio, sz);
}

uint32_t binder_cpp::io::get_ref() {
    return bio_get_uint32(bio);
}

binder_cpp::io::io() {
    bio = (binder_io *) malloc(sizeof(binder_io));
}

binder_cpp::io::io(binder_io *ptr) {
    if (ptr == nullptr)
        bio = (binder_io *) malloc(sizeof(binder_io));
    else
        bio = ptr;
}

void binder_cpp::io::put_data(const uint16_t *data, size_t size) {
    bio_put_data(bio, data, size);
}

void binder_cpp::io::put_data_x(const char *data, size_t size) {
    bio_put_data_x(bio, data, size);
}

uint16_t *binder_cpp::io::get_data(size_t *sz) {
    return bio_get_data(bio, sz);
}

binder_cpp::server::server(binder_cpp::binder &obj) {
    bs = access::get_state(obj);
    if (bs == nullptr)
        throw nullptr;
}

int binder_cpp::server::publish(uint32_t target, char *name, void *ptr) {
    if (bs == nullptr)
        throw nullptr;
    return svcmgr_publish(bs, target, name, ptr);
}

void binder_cpp::server::loop(binder_handler func) {
    if (bs == nullptr)
        throw nullptr;
    binder_loop(bs, func);
}

int binder_cpp::server::become_context_manager() {
    if (bs == nullptr)
        throw nullptr;
    return binder_become_context_manager(bs);
}

binder_cpp::client::client(binder_cpp::binder &obj) {
    bs = access::get_state(obj);
    if (bs == nullptr)
        throw nullptr;
}

uint32_t binder_cpp::client::lookup(uint32_t target, char *name) {
    if (bs == nullptr)
        throw nullptr;
    return svcmgr_lookup(bs, target, name);
}

int binder_cpp::client::call(binder_cpp::io &msg, binder_cpp::io &reply, uint32_t target, uint32_t code) {
    if (bs == nullptr)
        throw nullptr;
    auto ptr_msg = access::get_binder_io(msg), ptr_reply = access::get_binder_io(reply);
    if (ptr_msg == nullptr || ptr_reply == nullptr)
        throw nullptr;
    return binder_call(bs, ptr_msg, ptr_reply, target, code);
}

void binder_cpp::client::init_payload(binder_cpp::io &obj, uint32_t strict_mode) {
    obj.put_uint32(strict_mode);
}

void binder_cpp::client::init_payload(binder_cpp::io &obj, uint32_t strict_mode, uint16_t *str) {
    obj.put_uint32(strict_mode);
    obj.put_string16(str);
}

void binder_cpp::client::init_payload(binder_cpp::io &obj, uint32_t strict_mode, char *str) {
    obj.put_uint32(strict_mode);
    obj.put_string16_x(str);
}

void binder_cpp::client::init_payload(binder_cpp::io &obj, uint32_t strict_mode, void *ptr) {
    obj.put_uint32(strict_mode);
    obj.put_obj(ptr);
}
